package fr.cephb.locustree;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;

import fr.cephb.json.JSONBuilder;
import fr.cephb.json.JSONable;

public class LTNode
	implements JSONable,Serializable
	{
	public static final long NIL=-1L;
	private static final long serialVersionUID = 1L;
	public static final int CHILDREN_PER_NODE=10;
	long id=NIL;
	long parent_id=NIL;
	int start;
	int length;
	int level=0;
	int max_length_object=0;
	int count_entities_below=0;
	List<Long> sub_nodes_ids=null;
	Set<MappedObject> entities=null;
	//Map<Integer,ContentByType> type2content=null;
	
	public static class Binding
		extends TupleBinding<LTNode>
			{
			MappedObject.Binding mappedBinding= new MappedObject.Binding();
			@Override
			public LTNode entryToObject(TupleInput input)
				{
				LTNode node= new LTNode();
				node.id=input.readLong();
				node.parent_id=input.readLong();
				node.start=input.readInt();
				node.length=input.readInt();
				node.level=input.readInt();
				node.max_length_object=input.readInt();
				node.count_entities_below=input.readInt();
				
				int n= input.readInt();
				if(n>0)
					{
					node.sub_nodes_ids=new ArrayList<Long>(n);
					for(int i=0;i< n;++i)
						{
						node.sub_nodes_ids.add(input.readLong());
						}
					}
				n= input.readInt();
				if(n>0)
					{
					node.entities=new HashSet<MappedObject>(n);
					for(int i=0;i< n;++i)
						{
						node.entities.add(mappedBinding.entryToObject(input));
						}
					}
				return node;
				}
			
			@Override
			public void objectToEntry(LTNode node, TupleOutput output)
				{
				output.writeLong(node.id);
				output.writeLong(node.parent_id);
				output.writeInt(node.start);
				output.writeInt(node.length);
				output.writeInt(node.level);
				output.writeInt(node.max_length_object);
				output.writeInt(node.count_entities_below);
				
				if(node.sub_nodes_ids==null || node.sub_nodes_ids.isEmpty())
					{
					output.writeInt(0);
					}
				else
					{
					output.writeInt(node.sub_nodes_ids.size());
					for(int i=0;i< node.sub_nodes_ids.size();++i)
						{
						Long child_id= node.sub_nodes_ids.get(i);
						output.writeLong(child_id==null?NIL:child_id);
						}
					}
				if(node.entities==null || node.entities.isEmpty())
					{
					output.writeInt(0);
					}
				else
					{
					output.writeInt(node.entities.size());
					for(MappedObject s:node.entities)
						{
						this.mappedBinding.objectToEntry(s, output);
						}
					}
				}
			}
	
	
	public LTNode()
		{
		}
	
	@Override
	public String toJSON()
		{
		JSONBuilder b=new  JSONBuilder();
		b.put("id", getId());
		b.put("parent-id", this.parent_id);
		b.put("start", this.getStart());
		b.put("length", this.getLength());
		b.put("end", this.getEnd());
		b.put("level",getLevel());
		b.put("child-id", this.sub_nodes_ids);
		b.put("entities", this.entities);
		return b.getString();
		}
	
	/** returns wether level==0 */
	public boolean isRoot()
		{
		return level==0;
		}
	
	public int getLevel()
		{
		return level;
		}
	
	public long getId()
		{
		return id;
		}
	
	public int getStart()
		{
		return start;
		}
	
	/** size in bp */
	public int getLength()
		{
		return this.length;
		}
	
	public int getEnd()
		{
		return getStart()+getLength();
		}
	/*
	public int getChildResolution()
		{
		return getLength()/CHILDREN_PER_NODE;
		}*/
	
	public Long getSubNodeIdAt(int indexInList)
		{
		assert(isRoot() || indexInList< LTNode.CHILDREN_PER_NODE);
		return  (this.sub_nodes_ids==null || this.sub_nodes_ids.size()<=indexInList) ?
				null:
				this.sub_nodes_ids.get(indexInList);
		}
	
	public void setSubNodeIdAt(int indexInList,long child_id)
		{
		assert(isRoot() || indexInList< LTNode.CHILDREN_PER_NODE);
			
		if(this.sub_nodes_ids==null)
			{
			this.sub_nodes_ids=new ArrayList<Long>(indexInList+1);
			}
		//fill with null
		while(this.sub_nodes_ids.size()<= indexInList)
			{
			this.sub_nodes_ids.add(NIL);
			}
		this.sub_nodes_ids.set(indexInList,child_id);
		}
	
	public boolean hasEntities()
		{
		return this.entities!=null && !this.entities.isEmpty();
		}
	
	public boolean hasSubNodes()
		{
		return this.sub_nodes_ids!=null && !this.sub_nodes_ids.isEmpty();
		}
	
	public List<Long> getSubNodesId()
		{
		return this.sub_nodes_ids;
		}
	
	public Set<MappedObject> getEntities()
		{
		return entities;
		}
	
	public int getCountEntitiesBelow()
		{
		return count_entities_below;
		}
	public int getMaxEntitiesLength()
		{
		return max_length_object;
		}
	
	@Override
	public String toString()
		{
		return toJSON();
		}

	}
